﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Web.UI;
using System.ComponentModel.Design.Serialization;
using Doo.Controls;
using System.Collections;
using Doo.WebUI;

namespace Doo.Scripts
{
    public interface IUseScriptObject
    {
        string ScriptObjectID { get; set; }

        ScriptObject ScriptObject { get; set; }
    }

    public interface IClientEvent
    {

    }

    public interface IClientEvent<TEvents> : IClientEvent where TEvents : ClientEventsBase
    {
        TEvents ClientEvents { get; set; }
    }

    /// <summary>
    /// 客户端要暴露事件集合
    /// </summary>
    public abstract class ClientEventsBase : IUseScriptObject
    {
        /// <summary>
        /// 要使用的脚本对象
        /// </summary>
        public string ScriptObjectID { get; set; }
        private Controls.ScriptObject _sobject;
        [EditorBrowsable(EditorBrowsableState.Never)]
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Controls.ScriptObject ScriptObject
        {
            get { return _sobject ?? (string.IsNullOrEmpty(ScriptObjectID) ? null : Container.FindControl(ScriptObjectID) as Controls.ScriptObject); }
            set { _sobject = value; }
        }
        /// <summary>
        /// ClientEvent ClientNames
        /// </summary>
        protected List<string> eventnames;

        /// <summary>
        /// ClientEvent List
        /// </summary>
        protected List<ClientEvent> events;

        /// <summary>
        /// 所属页面
        /// </summary>
        protected Page Page { get; set; }

        /// <summary>
        /// Container
        /// Common Control.Parent
        /// </summary>
        protected Control Container { get; set; }

        public virtual Hashtable ToHashtable()
        {
            Hashtable ht = new Hashtable(eventnames.Count);
            int total = eventnames.Count;
            for (int i = 0; i < total; i++)
            {
                ht.Add(eventnames[i], events[i]);
            }
            return ht;
        }

        /// <summary>
        /// 用于自动联系事件的对象
        /// </summary>
        [EditorBrowsable(EditorBrowsableState.Never)]
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ScriptObject EventObject { get; set; }

        /// <summary>
        /// 使用自己所属的控件初始化，自动联系ScriptObject的event和自己的event
        /// </summary>
        /// <param name="me"></param>
        public virtual void Init(Control me)
        {
            if (!(me is IUseScriptObject))
            {
                throw new Exception("必须实现IUseScriptObject接口");
            }
            EventObject = (me as IUseScriptObject).ScriptObject;
            Init(me.Page, me.Parent);
        }

        /// <summary>
        /// 使用Page进行初始化
        /// </summary>
        /// <param name="page"></param>
        public virtual void Init(Page page, Control container)
        {
            this.Page = page;
            this.Container = container;

            this.eventnames = new List<string>();
            this.events = new List<ClientEvent>();

            //Search All ClientEvent
            var propInfos = this.GetType().GetProperties().Where(r => r.PropertyType == typeof(ClientEvent)).ToArray();
            foreach (var prop in propInfos)
            {
                //Find CustomAttribute
                var attrs = prop.GetCustomAttributes(typeof(ClientEventAttribute), false);
                if (attrs.Length == 0) continue;
                var evt = prop.GetValue(this, null) as ClientEvent;
                string evtname=(attrs[0] as ClientEventAttribute).EventName;
                if (evt == null) continue;
                if (EventObject != null)
                {
                    var ee = EventObject.GetEvent(evtname);
                    ee.Target = evt;
                }
                eventnames.Add(evtname);
                events.Add(evt);
            }

            page.InitComplete += new EventHandler(page_InitComplete);
        }

        void page_InitComplete(object sender, EventArgs e)
        {
            ScriptObject sobj = ScriptObject;
            
            //OnInitComplete,ResloveTarget
            foreach (var ce in this.events)
            {


                ce.OnInitComplete(this.Container, sobj);
            }

            this.OnInitComplete();
        }

        /// <summary>
        /// InitComplete
        /// </summary>
        public virtual void OnInitComplete()
        {

        }
    }

    /// <summary>
    /// 基础事件
    /// </summary>
    public class ClientEvents : ClientEventsBase
    {
        [Doo.WebUI.ClientEvent("load")]
        public ClientEvent OnLoad { get; set; }

        public ClientEvents()
        {
            this.OnLoad = new ClientEvent();
        }
    }

    /// <summary>
    /// 客户端事件
    /// </summary>
    [DefaultProperty("Target")]
    [Serializable]
    [TypeConverter(typeof(ClientEventTypeConverter))]
    public class ClientEvent : List<Doo.Controls.Function>
    {
        public ClientEvent() { }

        public ClientEvent(string target) { this._Target = target; }

        public ClientEvent(string objid, string target) { this._Target = target; this._ScriptObjectID = objid; }

        public Doo.Controls.Function this[string name]
        {
            get
            {
                return this.FirstOrDefault(r => r.Name == name);
            }

            set
            {
                var i = this.FindIndex(r => r.Name == name);
                this[i] = value;
            }
        }

        private bool inited = false;

        /// <summary>
        /// 在客户端触发
        /// </summary>
        public void ClientTrig(object[] paras)
        {
            this[0].ClientExec(paras);
        }

        /// <summary>
        /// 获取客户端方法[仅转换第一个方法]
        /// </summary>
        /// <returns></returns>
        public string GetClientReference()
        {
            return this[0].GetClientReference();
        }

        #region 属性定义
        protected string _ScriptObjectID = string.Empty;

        /// <summary>
        /// 脚本对象ID，只能在Init阶段进行修改
        /// </summary>
        public string ScriptObjectID
        {
            get { return this._ScriptObjectID; }
            set
            {
                if (inited) throw new Exception("在InitComplete阶段后无法修改脚本对象ID");

                this._ScriptObjectID = value;
            }
        }

        protected string _Target = string.Empty;

        /// <summary>
        /// 默认要触发的方法，只能在Init阶段进行修改
        /// </summary>
        public string Target
        {
            get { return _Target; }
            set
            {
                if (inited) throw new Exception("在InitComplete阶段后无法修改脚本对象ID");
                this._Target = value;
            }
        }
        #endregion

        /// <summary>
        /// On Page.InitComplete Trig This Event
        /// </summary>
        /// <param name="container"></param>
        /// <param name="parentObj"></param>
        public void OnInitComplete(Control container, ScriptObject parentObj)
        {
            this.inited = true;
            if (string.IsNullOrEmpty(this.Target)) return;

            Function func = null;
            if (string.IsNullOrEmpty(this._ScriptObjectID))
            {
                if (parentObj == null) throw new Exception("Error ScriptObjectID");
                func = parentObj.GetFunction(this._Target);
            }
            else if (_ScriptObjectID == ScriptObject.GlobalObject)
            {
                func = new Function { Name = _Target };
            }
            else
            {
                ScriptObject soo = container.FindControl(_ScriptObjectID) as ScriptObject;
                if (soo == null) throw new Exception("Error Target:" + _Target);
                func = soo.GetFunction(_Target);
            }
            if (func == null) throw new Exception("Error Target:" + _Target);

            this.Add(func);
        }

        /// <summary>
        /// 获取字符串表现形式
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            if (string.IsNullOrEmpty(_ScriptObjectID)) return this._Target;

            if (_ScriptObjectID == ScriptObject.GlobalObject) return "#" + _Target;

            return _ScriptObjectID + "." + this._Target;
        }

        /// <summary>
        /// Parse
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static ClientEvent Parse(string s)
        {
            if (s.StartsWith("#"))
            {
                return new ClientEvent(ScriptObject.GlobalObject, s.Substring(1));
            }

            int i = s.IndexOf('.');
            if (i < 0) return new ClientEvent(string.Empty, s);
            string objid = s.Substring(0, i);
            string funname = s.Substring(i + 1);
            return new ClientEvent(objid, funname);
        }
    }

    public class ClientEventTypeConverter : TypeConverter
    {
        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
            if (value is string && value != null)
            {
                return ClientEvent.Parse((string)value);
            }
            return base.ConvertFrom(context, culture, value);
        }

        public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == typeof(InstanceDescriptor))
            {
                ClientEvent ce = (ClientEvent)value;
                return new InstanceDescriptor(typeof(ClientEvent).GetMethod("Parse"),
                    new object[] { ce.ToString() }
                );
            }
            return base.ConvertTo(context, culture, value, destinationType);
        }
    }
}
